(function(E){
/*
核心模块：
	E.mod("Confirm")
	E.mod("Panel")
	E.mod("History")
	E.mod("Event")
	E.mod("Toolbar")
	E.mod("Data")
	E.mod("Selection")
*/

E.mod("Confirm", {
	config:{
		info:'您的内容还没有进行保存！'
	},
	set:function(msg){
		msg = msg || this.config.info;
		if(/function/i.test(typeof window.onbeforeunload)){
			return;
		}
		
	    window.onbeforeunload = function(event){
			//event=event || window.event;
	        //event.returnValue = msg;
			
			//Chrome 必须下面这样才有效，上面的对IE Firefox同样有效
			//但是这个对IE、Firefox同样有效，顾取消上面的代码
			return msg;
	    }
	},
	
	clear : function(){
	    window.onbeforeunload = null;
	}
});

E.mod("Panel", {
	eid:'' ,
	pid:'', //当前显示的面板id
	panels:[],//记录$Panel，键值是eid，在build时记录
	cmd_names:[],//当前执行的命令名称，键值是eid
	cmd_pres:[],//上一次执行的命令名称，键值是eid
	status:[],//当前面板是否处于显示状态，键值是eid
	
	//用以判断当前显示的面板是否是当前点击的插件plugin
	isShow:function(plugin){
		return plugin.name == this.cmd_names[plugin.eid];
	},
	
	//使用 $P 表示使用jQuery封装的 Panel 对象，下同
	tip:function(msg, eid){
		var self=this, $P = this.build(msg, eid);
		$P.find('div.panel_button').hide();
		$P.addClass("editor_panel_tip").fadeIn(100);
		setTimeout(function(){
			self.hide();
		}, 1000);
	},
	
	show:function(html, eid, plugin){
		var self=this, eid=eid||E.eid;
		if(this.cmd_names[eid]){
			this.cmd_pres[eid]=this.cmd_names[eid];
		}
		this.cmd_names[eid]=plugin.name;
		var $P=this.build(html, eid, plugin);
		return $P;
	},
	
	get:function(eid, name){
		var id=name?eid+"_"+name+"_panel": eid+"_panel";
		$P = this.panels[id];
		
		return $P;
	},
	
	getInputsValue:function(eid, name){
		var $P = this.get(eid, name);
		var inputs = {};
		$P.find(":input[name]").each(function(i){
			inputs[this.name] = $(this).val();
		});
		return inputs;
	},
	
	hide:function($btn){
		var eid=E.eid, Toolbar=E.mod("Toolbar");
		
		//如果不是显示状态则终止
		if(!this.status[eid]){return;}
		if(!$btn && E.btn[eid]){
			$btn = E.btn[eid][0];
		}
		
		var $P = $("#"+this.pid);
		
		if($P){
			$.IE6 ? $P.hide() : $P.fadeOut(200);
			this.status[eid] = false;
			if($btn) Toolbar.blur($btn, eid);
			return $P;
		}
		return 0;
	},
	
	/**
	注意 plugin是可选参数
	*/
	build:function(html, eid, plugin){
		var self=this, eid=eid||E.eid, width=600,config={width:width}, option=E.options[eid],
		$btn, $bar=$("#"+eid+"_toolbar"),History=E.mod("History");
		
		if(E.btn[eid]) $btn = E.btn[eid][0];
		
		if(plugin){
			pid=eid+"_"+plugin.name+"_panel", $P=$("#"+pid);
			if(plugin['config']){
				config = $.extend(config, plugin['config']);
			}
		}else{
			pid=eid+"_panel", $P=$("#"+pid);
		}
		
		if(this.pid) $("#"+this.pid).hide();
		this.pid = pid;
		width = (config.width <= $bar.width())? config.width :$bar.width()-3;
		
		if($P.size()>0){
			$P.show();
			
			//如果是系统命令，一般是提示信息，需要实时更换内容，否则是插件则不需要重新构建内容
			if(!plugin){
				$P.html(html);
			}
			
		}else{
			html = html || '';
			html = '<div class="panel_body">'+html+'</div>';
			
			//是否显示面板下面的确定和关闭按钮
			if(plugin && typeof plugin.ok=='function'){
				html += '<div class="panel_button">';
				html += '<input name="submit" type="button" value=" 确 定 "/>';
				html += '<input name="close" type="button" value=" 关 闭 "/>';
				html += '</div>';
			}
			
			$P = $($.create("div"));
			$P.appendTo("body");
			$P.attr("id", pid).addClass("editor_panel");
			$P.html(html);
			
			//绑定关闭、确定按钮的点击事件
			if(plugin && typeof plugin.ok=='function'){
				$P.find("div.panel_button input[name=close]").unbind('click').click(function(){
					self.hide();
					E.focus();
				});
				$P.find("div.panel_button input[name=submit]").unbind('click').click(function(){
					var dom=E.documents[eid];
					E.focus();
					Data=E.mod("Data");
					if (Data){
						if(plugin.ok(eid, Data)) {
							self.hide();
						}
						History && History.add(dom, eid);
					}
				});
			}
		
		
			//面板的宽度不能大于编辑器窗口的宽度
			$P.css({width:width});
			
			config.height && $P.find("div.panel_body").css({height:config.height});
			
			//在面板上面点击时，拒绝隐藏面板，否则会触发绑定在 document 上面的点击隐藏面板操作
			$P.click(function(){return false});
		}
		
		//无论是否存在都需要重新定位
		var top,left,offset=$bar.offset();
		top = offset.top + $bar.outerHeight();
		
		if($btn && plugin){
			if(config.left){
				//强制和按钮的左侧对齐
				left = $btn.offset().left;
				
			}else if(width>300){
				//面板宽度大于300则居中显示，小于等于300则和按钮左侧或右侧对齐
				left = offset.left + ($bar.width() - width)/2 - 3;
			}else{
				//默认左侧对齐
				left = $btn.offset().left;
				//判断是否右侧对齐更加合适
				if(left + width > offset.left + $bar.width()){
					left =left - width + $btn.width() -9;
				}
			}
		}else{
			//面板宽度大于300则居中显示，小于等于300则和按钮左侧或右侧对齐
			left = offset.left + ($bar.width() - width)/2 - 3;
			
		}
		
		//将当前面板记录到缓存数组当中
		this.panels[pid] = $P;
		this.status[eid] = true;
		
		/*
		如果 body 是 relatiove 定位，很肯能导致面板定位不准确。
		理想情况是body居中，则计算一下body 和 窗口宽度差然后除以2，如果小于0，则用left减去即可
		*/
		if($("body").css("position") == "relative"){
			var _W = ($(window).width() - $("body").width())/2;
			if (_W>0) left -= _W;
		}
		
		return $P.css({top:top,left:left});
	}
});

E.mod("History", {
	stack:[], //历史记录，二维数组，一维键值是eid，二维数组从 0 开始
	pointer:[], 
	last:[], //记录最后修改的数据内容，以判断是否禁用 "重做" 按钮
	ranges:[], //尝试记录历史版本光标的位置
	
	add: function(dom, eid){
		var Confirm=E.mod('Confirm');
		
		eid=eid||E.eid, dom = dom || E.documents[eid];
		if(this.pointer[eid] == null) this.pointer[eid] = -1;
		if(this.stack[eid] == null) {
			this.stack[eid]=[];
		}
		
		var html=dom.body.innerHTML, stack=this.stack[eid],
			p=this.pointer, last=this.last, Toolbar=E.mod("Toolbar");
		
		if (html == stack[p[eid]]){
			return;//和当前内容相同，即没有修改
		}
		
		if(stack[p[eid]]){
			var len = Math.abs(html.length - stack[p[eid]].length);//数据差异
			if ($.IE && len<5){
				return;//IE下，和当前内容的差异太小
			}
		}
		
		//设置离开提示信息
		if(Confirm && p[eid] > -1){Confirm.set();}
		
		last[eid] = html;
		
		if(p[eid]<0){
			stack[++p[eid]]=html;
		}else{
			stack[++p[eid]] = $.IE ? html:"";
		}
		Toolbar.disable("Undo", false, eid);
		Toolbar.disable("Redo", true, eid);
		
		if(p[eid] < stack.length-1){
			stack.splice(p[eid]+1, stack.length-1-p[eid]);
		}
	},
	
	Undo:function($btn, dom, eid){
		//dom.execCommand('undo', false, null); //系统默认
		var stack=this.stack[eid], p=this.pointer, html,Toolbar=E.mod("Toolbar");
		html=stack[--p[eid]], last=this.last;
		if($.IE){
			dom.body.innerHTML = html;
		}else{
			E.execCommand('Undo');
		}
		Toolbar.disable("Redo", false, eid);
		
		if (dom.body.innerHTML == stack[0]){
			p[eid]=0;
		}
		
		if (p[eid]<1){
			Toolbar.disable("Undo", true, eid);
		}
		
		return 1;
	},
	
	Redo:function($btn, dom, eid){
		//dom.execCommand('redo', false, null);
		var stack=this.stack[eid], p=this.pointer, html,
			last=this.last, Toolbar=E.mod("Toolbar");
			
		html=stack[++p[eid]];
		
		if($.IE){
			dom.body.innerHTML = html;
		}else{
			E.execCommand('Redo');
		}
		Toolbar.disable("Undo", false, eid);
		
		if (p[eid] >= stack.length-1 || last[eid] === dom.body.innerHTML){
			Toolbar.disable("Redo", true, eid);
		}
		return 1;
	}
});

E.mod("Event", {
	temp:[],
	listener:{
		mousedown:[],
		mouseup:[],
		click:[],
		keydown:[],
		keyup:[],
		keypress:[],
		paste:[]
	},
	
	add:function(type, fn){
		if(typeof fn == 'function'){
			if(this.listener[type]){
				this.listener[type].push(fn);
			}else{
				alert("Event.add(type, fn), type="+ type +" 不存在。");
			}
		}else{
			alert("Event.add(type, fn), fn必须是一个函数。");
		}
	},
	//执行所以绑定的事件
	run:function(type,event,eid,dom){
		var fns=this.listener[type];
		
		if (!/function|object/i.test(typeof fns)){
			return;
		}
		for(var i=0;i<fns.length;i++){
			fns[i](event, eid, dom);
		}
	},
	
	bind: function(el, eventName, listener){
		if(el.addEventListener){
			el.addEventListener(eventName, listener, false);
		}else if (el.attachEvent){
			el.attachEvent('on' + eventName, listener);
		}
		return this;
	},
	remove: function(el, eventName, listener){
		if(el.removeEventListener){
			el.removeEventListener(eventName, listener, false);
		}else if (el.detachEvent){
			el.detachEvent('on' + eventName, listener);
		}
		return this;
	},
	
	//阻止事件，比如在执行一些快捷键时如果和浏览器的快捷键冲突时，就需要这些做
	stop:function(event){
		event=event || window.event;
		if ($.IE) {
			event.cancelBubble = true;
			event.returnValue = false;
		}else{
			event.preventDefault();
			event.stopPropagation();
		}
	},
	
	cloneEvent: function(event){
		if(typeof $ == "undefined"){ //FF下有时莫名提示 $ 未定义
			return event;
		}
		if($.IE){
			return {
				keyCode: event.keyCode,
				ctrlKey: event.ctrlKey,
				altKey: event.altKey,
				shiftKey: event.shiftKey
			}
		}else return event;
	},
	
	/*
	编辑器的初始化事件，一般有回车、键盘快捷键等，对事件进行延时处理，防止频繁重复执行。
	在编辑器创建时调用。
	*/
	init: function(dom, eid){
		var self=this, Panel=E.mod("Panel"),options=E.options[eid],AutoHeight=E.plugin("AutoHeight");
		//应该屏蔽一些事件，
		
		this.add('mouseup', this.fn_mouseup);
		this.add('click', this.fn_click);
		this.add('keydown', this.fn_keydown);
		this.add('keyup', this.fn_keyup);
		this.add('keypress', this.fn_keypress);
		this.add('paste', this.fn_paste);
		
		this.bind(dom, 'keydown', function(event){
			event=event || window.event;
			var code = event.keyCode;
			key = String.fromCharCode(event.keyCode || event.charCode || event.which).toUpperCase();
			
			//如果按下了 Ctrl 键则取消按键事件，改为可能的快捷键事件
			if (event.ctrlKey && /[biuzy]/i.test(key)) {
				self.stop(event);
			}
			
			clearTimeout(self.temp['keydown']);
			//延迟处理后 IE 下面 event 在 setTimeout里面就失效了会出错，必须克隆一个
			var EVENT = self.cloneEvent(event);
			self.temp['keydown']=setTimeout(function(){
				self.keydown(EVENT, eid, dom);
			}, 200);
		});
		
		this.bind(dom, 'keyup', function(event){
			event=event || window.event;
			clearTimeout(self.temp['keyup']);
			
			var EVENT = self.cloneEvent(event);
			self.temp['keyup']=setTimeout(function(){
				self.keyup(EVENT, eid, dom);
			}, 300);
		});
		
		this.bind(dom, 'click', function(event){
			event=event || window.event;
			Panel.hide();
			clearTimeout(self.temp['click']);
			var EVENT = self.cloneEvent(event);
			self.temp['click']=setTimeout(function(){
				self.click(EVENT, eid, dom);
			}, 200);
		});
		
		this.bind(dom, 'mouseup', function(event){
			clearTimeout(self.temp['mouseup']);
			var EVENT = self.cloneEvent(event);
			self.temp['mouseup']=setTimeout(function(){
				self.mouseup(EVENT, eid, dom);
			}, 200);
			
		});
		
		this.bind(dom, 'keypress', function(event){
			clearTimeout(self.temp['keypress']);
			var EVENT = self.cloneEvent(event);
			self.temp['keypress']=setTimeout(function(){
				self.keypress(EVENT, eid, dom);
			}, 200);
			
		});
		
		//将工具栏恒定在窗口顶部
		var FixedBar = E.plugin("AutoFixedToolbar");
		if(typeof FixedBar == "function" && options.autoHeight){
			$(window).scroll(function(){
				FixedBar(eid);
			});
		}
		
		//点击页面或窗口滚动时隐藏面板
		$(document).click(function(event){
			if($.IE)Panel.hide();
			else if(event.button != 2)Panel.hide();
		});
		$(window).scroll(function(){
			Panel.hide();
		});
		
		//编辑器初始化后根据内容的高度改变编辑器的高度
		if (options.autoHeight && AutoHeight){
			setTimeout(function(){
				AutoHeight(eid);
			}, 100);
		}
		
		//检测表单的提交事件
		var textarea = options["textarea"], Data=E.mod("Data");
		if(textarea && $(textarea).size() == 1 ){
			var Form=$(textarea).parents("form");
			if(Form.size() == 1){
				Form.submit(function(){
					Data.saveToInput(eid);
				});
			}
		}
	},
	
	mousedown: function(event, eid, dom){
		this.run('mousedown',event,eid,dom);
	},
	
	mouseup: function(event, eid, dom){
		this.run('mouseup',event,eid,dom);
	},
	
	fn_mouseup:function(event, eid, dom){
		var self=this, Selection=E.mod("Selection"), Data=E.mod("Data");
		E.ranges[eid] = Selection.saveRange();
	},
	
	click: function(event, eid, dom){
		this.run('click',event,eid,dom);
	},
	fn_click: function(event, eid, dom){
		var self=this, Data=E.mod("Data");
		E.mod("Toolbar").updateState(eid, dom);
		if($.IE && dom.activeElement.parentNode.nodeType === 9){ // 点击在 doc 上
			self._focusToEnd();
		}
	},
	keyup: function(event, eid, dom){
		this.run('keyup',event,eid,dom);
	},
	fn_keyup: function(event, eid, dom){
		var self=this, Selection=E.mod("Selection"),
			AutoHeight=E.plugin("AutoHeight"),
			options=E.options[eid], Data=E.mod("Data"),
			History = E.mod("History"),
			Toolbar=E.mod("Toolbar");
		
		Toolbar.updateState(eid, dom);
		E.ranges[eid] = Selection.saveRange();
		
		if (options.autoHeight && AutoHeight){
			AutoHeight(eid);
		}
		
		//同步数据到关联的表单项
		Data.sync(eid);
		History && History.add(dom, eid);
	},
	keydown: function(event, eid, dom){
		
		this.run('keydown',event,eid,dom);
	},
	fn_keydown: function(event, eid, dom){
		var self=this, key, cmd=null, value=null;
		key = String.fromCharCode(event.keyCode || event.charCode || event.which).toUpperCase();
		
		//回车事件
		if (event.keyCode == 13 && !event.ctrlKey && !event.altKey){
			
		}
		
		if (event.keyCode == 13 && !event.ctrlKey && !event.altKey && !event.shiftKey){
			
		}

		if (event.ctrlKey) {
			switch (key) {
			    case 'B': cmd = "Bold"; break;
			    case 'I': cmd = "Italic"; break;
			    case 'U': cmd = "Underline"; break;
			  	case 'Z': cmd = "Undo"; break;
			   	case 'Y': cmd = "Redo";break;
			}
			
			if(cmd){
				var Plugin = E.plugin(cmd);
				
				if(Plugin){
					$btn = $("#"+eid+"_"+cmd);
					Plugin.click($btn, dom, eid);
				}
				
			}
		}else if (event.altKey){
			
		}
		
	},
	keypress: function(event, eid, dom){
		
		this.run('keypress',event,eid,dom);
	},
	fn_keypress: function(event, eid, dom){
		var self=E.mod("Event");
		self._forcePtag(event, eid, dom);
	},
	scroll: function(event, eid){
		
	},
	
	paste: function(event, eid){
		
		this.run('paste',event,eid,dom);
		return false;
	},
	fn_paste: function(event, eid){
		if($.IE){
			var Selection=E.mod('selection'), text=window.clipboardData.getData("text");
			if (!text) return false;
			var isAccess = window.clipboardData.setData("Text", text);
			
			//当访问window.clipboardData时，IE7+浏览器会询问用户是否允许访问剪切板，如果用户选择不允许，
			//则程序会到此终止，下面的代码不会被执行
			if (!isAccess){
				//弹出粘帖窗口
				
			}else{
				//所以，IE7+往往看不到这个代码执行
				text=window.clipboardData.getData("text");
				text = text.replace(/\n/g, '<br>');
				Selection && Selection.set(text);
			}
		}else{
			//弹出粘帖窗口
			
			alert(22);
		}
		
		return false;
	},
	//将光标定位到内容尾部
	_focusToEnd: function() {
		E.focus();
		var eid=E.eid, lastChild=E.documents[eid].body.lastChild, range =E.ranges[eid];
		if($.IE) {
			try{ // 有时会报错：编辑器 ie 下，切换源代码，再切换回去，点击编辑器框内，有无效指针的JS错误
				range.moveToElementText(lastChild);
			}catch(ex){ }
			range.collapse(false);
			range.select();
		}else{
			try{
				range.setEnd(lastChild, lastChild.childNodes.length);
			}catch(ex){ }
			range.collapse(false);
		}
	},
	_forcePtag: function(ev, eid, dom){
		if(ev.keyCode!=13||ev.shiftKey||ev.ctrlKey||ev.altKey)return true;
		var options=E.options[eid], Selection=E.mod("Selection");
		
		function getParent(tag){
			var range=Selection.getRange(), p;

			if(!$.IE){
				p = range.commonAncestorContainer;
				if(!range.collapsed){
					if(range.startContainer == range.endContainer
						&& range.startOffset - range.endOffset < 2
						&& range.startContainer.hasChildNodes()
					){
						p =range.startContainer.childNodes[range.startOffset];
					}
				}
			}else {
				p=range.item?range.item(0):range.parentElement();
			}
			tag=tag ?tag :'*';
			p=$(p, dom);
			if(!p.is(tag)){ p=$(p, dom).closest(tag);}
			return p;
		}
		
		
		var pNode=getParent('p,h1,h2,h3,h4,h5,h6,pre,address,div,li');
		if(options.forcePtag){
			if(pNode.length==0){
				E.execCommand('formatblock', false, '<p>');
			}
		}else{
			E.pasteHTML('<br />');
			return false;
		}
	}
});

E.mod("Toolbar", {
	//点击某按钮后进行锁定，直到执行完毕解锁
	locking:0,
	unlock:function(){
		this.locking = 0;
		return this;
	},
	locked:function(){
		this.locking = 1;
		return this;
	},
	isLock:function(){
		return this.locking;
	},
	
	toolbars:{
		/*
		'default':[
			'Source','Undo','Redo','Font','Bold','Italic','Underline',
			'ForeColor','BackColor','InsertOrderedList','InsertUnorderedList','StrikeThrough',
			'JustifyLeft','JustifyCenter','JustifyRight',
			'Outdent','Indent','Image', 'SubScript','SuperScript','Link'
			,'PasteText','PasteWord','RemoveFormat','Filter'
		],
		*/
		
		'default':[
			'Source','Undo','Redo','PasteText','PasteWord','Font','MenuBold','MenuJustifyLeft',
			'ForeColor','BackColor','Image', 'Link'
			,'RemoveFormat','Filter'
		],
		
		'basic':['Font','Bold','Italic','Underline','ForeColor'],
		
		'all':[
			'Source','Save','Undo','Redo','Font','Bold','Italic','Underline',
			'ForeColor','BackColor','InsertOrderedList','InsertUnorderedList','StrikeThrough',
			'JustifyLeft','JustifyCenter','JustifyRight',
			'Outdent','Indent','Image','About','JustifyFull', 'SubScript','SuperScript',
			'Find','Replace','Link','PasteText','PasteWord','RemoveFormat','SpecialChar'
		]
	},
	
	/**
	由 E.exec() 调用
	点击工具条按钮，使按钮在选中和取消两种状态当中切换，并调用插件的click方法
	*/
	click: function($btn, eid, dom, Plugin, action){
		eid = eid || E.eid;
		if(!action || !Plugin[action]){
			action = 'click';
		}
		$btn = $btn || E.btn[eid][0];
		$btn.toggleClass('checked');
		
		//当点击的按钮呢上次的不是同一个按钮时，则取消上次点击按钮的选中状态
		var Panel=E.mod("Panel"), btn=E.btn[eid];
		if(btn[1] && btn[0].attr('name') != btn[1].attr('name')){
			this.blur(btn[1]);
		}
		
		//下面是对扩展插件执行的代码
		var name=$btn.attr("name");
		//对文字颜色和背景颜色特殊处理
		if(/ForeColor|BackColor/i.test(name)){
			$btn.hasClass("checked")?
			$btn.find('img').addClass('checked'):
			$btn.find('img').removeClass('checked');
		}
		
		//对于基础插件，到此结束执行
		if( (Plugin["type"] && Plugin["type"]=="base") 
			|| (action == 'click' && $btn.children('a.menu').size() >0)
		){
			Panel.hide($btn);
			return Plugin.click($btn, dom, eid);
		}
		
		var $P=Panel.get(eid, name); //$P 当前插件的面板
		
		//如果当前点击的插件处于显示状态，将将其隐藏
		if(Panel.isShow(Plugin) && $P.size() && $.isShow($P)){
			Panel.hide($btn);
			return 1;
		}else{
			//执行插件的 action 方法
			return Plugin[action]($btn, dom, eid);
		}
	},
	
	//取消按钮的选中状态
	blur: function($btn, eid){
		eid = eid || E.eid;
		$btn = $btn || E.btn[eid][0];
		if($btn && $btn.removeClass) {
			$btn.removeClass('checked');
			
			var name=$btn.attr("name");
			if(/ForeColor|BackColor/i.test(name)){
				$btn.hasClass("checked")?
				$btn.find('img').addClass('checked'):
				$btn.find('img').removeClass('checked');
			}
		}
		return this;
	},
	
	//检测光标所在位置是否执行过当前命令，按点击内容、工具栏按钮以及内容keyup时执行
    updateState: function(eid, dom){
        var cmd, state=false, cmds=[
			'Bold','Italic','Underline','StrikeThrough',
			'InsertOrderedList','InsertUnorderedList',
			'JustifyLeft','JustifyCenter','JustifyRight','JustifyFull',
			'Outdent','Indent','SubScript','SuperScript'
        ];
		
        for (var i=0;i<cmds.length;i++){
            cmd=cmds[i];
            try{
                state = dom.queryCommandState(cmd);
            }catch(e){
				state=false;
			}
			
			if(state){
				$("#"+eid+"_"+cmd+", #icon_"+eid+"_"+cmd).addClass("checked");
			}else{
				$("#"+eid+"_"+cmd+", #icon_"+eid+"_"+cmd).removeClass("checked");
			}
        }
		return this;
    },
	
	//使按钮可用或不可用
	disable: function(names, disabled, eid){
		eid = eid||E.eid;
		names=names.replace(/\s+/g,'');
		names="#"+eid+"_"+names.replace(/,/g,",#"+eid+"_");
		var btns = $("#"+eid+"_toolbar").children(names);
		disabled?btns.addClass("disabled"):btns.removeClass("disabled");
		return this;
	},
	
	/*
	* 构造下拉菜单的html
	* arrList=[]
	*/
	getMenuHtml: function(arrList, option){
		var html='', name, icon, title='', bgpos='', btns, eid=option.eid;
		var Lang = option.Lang;
		
		for(i=0; i<arrList.length; i++){
			name = arrList[i];
			icon = E.icons[option.icon][name];
			if (!icon){
				continue;
			}
			
			if(typeof Lang[name] == 'string'){
				title = Lang[name];
			}else if(typeof Lang[name] == 'object'){
				if (Lang[name].length){
					title = Lang[name][0] + Lang[name][1];
				}else{
					title = Lang[name]['title'];
				}
			}
			
			/*
			// 不再单独使用X 或 Y，均使用XY指定坐标位置
			if (icon['Y']){
				bgpos = '0px -'+(icon['Y'] -1)*16+'px';
			}else if(icon['X']){
				bgpos = '-'+(icon['X'] -1)*16+'px 0px';
			}else 
			*/
			if(icon['XY']){
				bgpos = icon['XY'];
			}else{
				bgpos='';
			}
			
			html +='<tr name="'+name+'" id="'+ eid +'_'+ name +'"><td class="img">';
			if (bgpos) html +='<img id="icon_'+ eid +'_'+ name +'" class="'+option.icon+'" style="background-position:'+bgpos+'" src="'+option.base+'/themes/spacer.gif" border="0"/>';
			html +='</td><td class="text"><a href="javascript:void()" name="000000">'+title+'</a></td></tr>';
		}
		html ='<table border="0" class="menu" cellPadding="0" cellSpacing="0">'+ html +'</table>';
		return html;
	},
	
	build: function(option){
		var html='', name, icon, title='', bgpos='', btns, eid=option.eid;
		var Lang = option.Lang;
		
		switch(typeof option.toolbar){
			case 'string':
				btns = this.toolbars[option.toolbar];break;
			case 'object':
				btns = option.toolbar;break;
			default:
				alert('option.toolbar is error.');
				return '';
		}
		
		for(i=0; i<btns.length; i++){
			name = btns[i];
			icon = E.icons[option.icon][name];
			if (!icon){
				continue;
			}
			
			if(typeof Lang[name] == 'string'){
				title = Lang[name];
			}else if(typeof Lang[name] == 'object'){
				if (Lang[name].length){
					title = Lang[name][0] + Lang[name][1];
				}else{
					title = Lang[name]['title'];
				}
			}
			/*
			if (icon['Y']){
				bgpos = '0px -'+(icon['Y'] -1)*16+'px';
			}else if(icon['X']){
				bgpos = '-'+(icon['X'] -1)*16+'px 0px';
			}else 
			*/
			if(icon['XY']){
				bgpos = icon['XY'];
			}else{
				bgpos='';
			}
			
			if (bgpos){
				//有图标的按钮
				html+='<div id="'+eid+'_'+name+'" title="'+ title +'" class="'+name+'" name="'+name+'"';
				if (icon['width'] && icon['menu'] ){
					html+='style="width:'+ icon['width']+'px;"';
				}
				
				//图标有宽度，右边可能需要一个下拉按钮
				if (icon['width'] && icon['menu'] ){
					html+='><a class="img" href="javascript:void(\''+name+'\');" onclick="return false"><img class="'+
						option.icon+' '+name+'" name="'+name+'" style="background-position:'
						+bgpos+'" src="'+option.base+'/themes/spacer.gif" border="0"/></a>';
					html+='<a class="menu '+option.icon+'_menu" href="javascript:void(\''+name+'\');" onclick="return false"></a>';
				}else{
					//仅有一个图标的按钮
					html+='><a class="img '+name+'" href="javascript:void(\''+name+'\');" onclick="return false"><img class="'+option.icon+'" name="'+name+'" style="background-position:'
						+bgpos+'" src="'+option.base+'/themes/spacer.gif" border="0"/></a>';	
				}
				html+='</div>';
			}else{
				//没有图标显示文字的按钮
				var width=icon['width'] - 11;
				html+='<div id="'+eid+'_'+name+'" class="'+name+'" name="'+name+'" title="'+ title +'" style="width:'+ icon['width']+'px;">'
					+'<a class="img" href="javascript:void(\''+name+'\');" onclick="return false" style="width:'+width+'px"><span>'+ title +'</span></a>'
					
				if (icon['menu']){
					html+='<a class="menu '+option.icon+'_menu" href="javascript:void(\''+name+'\');" onclick="return false"></a>';
				}
				html+='</div>';
			}
		}
		
		return html;
	}
});


E.mod("Data", {
	arrFontsize:[{n:'xx-small',wkn:'x-small',s:'8pt',t:'极小'},{n:'x-small',wkn:'small',s:'10pt',t:'特小'},{n:'small',wkn:'medium',s:'12pt',t:'小'},{n:'medium',wkn:'large',s:'14pt',t:'中'},{n:'large',wkn:'x-large',s:'18pt',t:'大'},{n:'x-large',wkn:'xx-large',s:'24pt',t:'特大'},{n:'xx-large',wkn:'-webkit-xxx-large',s:'36pt',t:'极大'}],
	
    escape : function(html) {
        html = html.replace(/&/g, "&amp;");
        html = html.replace(/</g, "&lt;");
        html = html.replace(/>/g, "&gt;");
        html = html.replace(/ /g, "&nbsp;");
        html = html.replace(/\xA0/g, "&nbsp;");
        html = html.replace(/\x20/g, " ");
        return html;
    },
	
	//获取编辑器的内容
	get: function(eid){
		eid = eid || E.eid;
		var body = E.documents[eid].body, html=body.innerHTML;
		html = this.replace(html);
		html = this.xhtml(html);
		return html;
	},
	
	//设置编辑器的内容
	set: function(html, eid){
		eid = eid || E.eid;
		var body = E.documents[eid].body, history = E.mod("History"), dom=E.documents[eid];
		if(body.innerHTML != html){
			body.innerHTML = this.replace(html);
			history && history.add(dom, eid);
		}
		return 1;
	},
	
	/*
	* 在IE下将所有标签会自动转为大写，这里使用正则方式再转为小写。
	* 将形如如size=4的属性改为 size="4"。
	* 使内容符合 xhtml 格式。
	*/
	xhtml: function(html){
		//对于个别没有结束标签的，加上反斜杠
		html = html.replace(/<(br|hr)>/ig, '<$1 />');
		
		//对于没有反斜杠结尾的img标签，加上反斜杠
		html = html.replace(/<(img[^>]*)([^\/])>/ig, '<$1$2 />');
		
		if(!$.IE){return html;}
		
		//将形如<A>或</A>这样标签的转为<a></a>
		var re=/(<\/?)([A-Z]+)>/g;
		html = html.replace(re, function($0, $1, $2){
			return $1+$2.toLowerCase()+'>';
		});
		
		//转换带属性的标签，和大写的属性名，以及没有引号的属性值
		//注意：replace 里面的 function 的参数必须从$0开始，即便是仅需要$1，$2等
		re=/<([A-Z]+)([^>]*)>/g;
		html = html.replace(re, function($0,$1,$2,$3){
			
			//将大写的属性名转为小写
			$2 = $2.replace(/\s([A-Z]+)=/g, function($_0, $_1){
				return ' '+$_1.toLowerCase() + '=';
			});
			
			//将不带双引号的属性值加上双引号
			$2 = $2.replace(/\s+(\w+=)([\w]+)/g, function($_0, $_1, $_2){
				return ' '+$_1 + '"' + $_2 + '"';
			});
			return '<'+$1.toLowerCase()+ $2 +'>';
		});
		

		return html;
	},
	
	
	/*
	* 检查双标签是否完整
	*/
	checkTag: function(html){
		var tip='', re=/<(\/?\w+)/ig, tags=[], arr, name;
		while((arr = re.exec(html)) != null){
			name=arr[1].toLowerCase();
			//忽略一些单标签
			if(/^img|br|hr|input|link|meta|param$/i.test(name)) continue;
			
			if(name.indexOf('/') == -1){
				tags.push(name);
			}else{
				if('/'+tags[tags.length-1] == name){
					tags.pop();
				}
			}
		}
		
		if(tags.length >6){
			tip ='提示：\n'+tags.length+'个标签 '+ tags.slice(0,6).join(', ') +'... 未闭合，请检查。';
		}else if(tags.length){
			tip ='提示：\n标签 '+ tags.join(', ') +' 未闭合，请检查。';
		}
		return tip;
	},
	
	//进行一些常规替换
	replace: function(html){
		//替换所有的font标签
		//html = html.replace(/(<\/?font.*?>)+/ig, '');
		
		//替换掉所有的空标签
		html = html.replace(/<(div|strong|span|a|p)[^>]*?>\s*<\/\1>/ig, '');
		
		//替换掉不在同一行上strong标签
		html = html.replace(/<strong([^>]*?>.+?)<\/strong>/ig, '<_strong_$1</_strong_>');
		html = html.replace(/(<\/?strong.*?>)+/ig, '');
		html = html.replace(/_strong_/ig, 'strong');
		
		//from xheditor cleanHTML()
		html = html.replace(/<\??xml(:\w+)?(\s+[^>]*?)?>([\s\S]*?<\/xml>)?/ig, '');
		html = html.replace(/<\/?(html|head|body|meta|title)(\s+[^>]*?)?>/ig, '');

		//if(!_this.settings.linkTag)
		html = html.replace(/<link(\s+[^>]*?)?>/ig, '');
		//if(!_this.settings.internalScript)
		html = html.replace(/<script(\s+[^>]*?)?>[\s\S]*?<\/script>/ig, '');
		//if(!_this.settings.inlineScript)
		html=html.replace(/(<\w+)(\s+[^>]*?)?\s+on(?:click|dblclick|mousedown|mouseup|mousemove|mouseover|mouseout|mouseenter|mouseleave|keydown|keypress|keyup|change|select|submit|reset|blur|focus|load|unload)\s*=\s*(["']?).*?\3((?:\s+[^>]*?)?\/?>)/ig,'$1$2$4');
		//if(!_this.settings.internalStyle)
		html = html.replace(/<style(\s+[^>]*?)?>[\s\S]*?<\/style>/ig, '');
		//if(!_this.settings.inlineStyle)
		html=html.replace(/(<\w+)(\s+[^>]*?)?\s+(style|class)\s*=\s*(["']?).*?\3((?:\s+[^>]*?)?\/?>)/ig,'$1$2$4');
		html=html.replace(/<\/(strong|b|u|strike|em|i)>((?:\s|<br\/?>|&nbsp;)*?)<\1(\s+[^>]*?)?>/ig,'$2');
		
		if(!$.IE){
			//非IE浏览器，则需将 strong 和 em 标签转为 b 和 i，
			//否则加粗、斜体操作对于已经存在的strong和em不起作用。
			html = html.replace(/<(\/?)(strong|em)>/ig, function($0, $1, $2){
				var o={strong:'b', em:'i'};
				return '<'+$1+o[$2]+'>';
			});
		}
		return html;
	},
	
	//在光标处插入内容
	insert: function(html, mode){
		var selection = E.mod("Selection");
		if(!mode) html = this.escape(html);
		selection && selection.set(html);
	},
	
	insertText: function(html){
		var selection = E.mod("Selection");
		html = this.escape(html);
		selection && selection.set(html);
	},
	
	insertHtml:function(html){
		var selection = E.mod("Selection");
		selection && selection.set(html);
	},
	
	sync:function(eid, flag){
		//sync 同步数据到关联的表单项
		eid = eid || E.eid;
		var textarea = E.options[eid]["textarea"], content;
		if(textarea) {
			if(flag){
				//设置编辑器的内容
				content=$(textarea).val();
				this.set(content);
			}else{
				/*
				* 在Chrome下，给表单项同步数据会导致撤销、重做操作失效。
				* 为了兼容性，在 Chrome 下同步数据方式考虑换一下。
				* 在IE6下为了提高性能同样不再实时同步数据。
				*/
				if($.Chrome || $.IE6){return;}
				
				//将编辑器的内容同步到表单项当中
				content=this.get(eid);
				$(textarea).val(content);
			}
		}else{
			E.tip("和"+eid+"关联的input="+textarea+"不存在",eid);
		}
	},
	
	saveToInput:function(eid){
		eid = eid || E.eid;
		var textarea = E.options[eid]["textarea"], content;
		content=this.get(eid);
		$(textarea).val(content);
	},

	/**
         * 过滤数据
         */
	filterData: function(data) {
		
	}
});


E.mod("Selection", {
	dom:null,
	win:null,
	init:function(){
		this.dom = E.dom;
		this.win = E.windows[E.eid];
	},
	
	get:function(mode){
		this.init();
		var html = '', dom = this.dom, win = this.win;
		
		if(win.getSelection){
			html=win.getSelection(); // only text
		}else if(dom.getSelection) {
			html=dom.getSelection(); // only text
		}else if(dom.selection){// only ie
			html=(mode)?dom.selection.createRange().htmlText:dom.selection.createRange().text;
		}
		return html;
	},
	
	getHtml:function(){
		return this.get(1);
	},
	
	getSelection: function(win, dom){
		return win.getSelection ? win.getSelection() : dom.selection;
	},
	
	set:function(html){
		if (html === '') return;
		this.init();
		E.focus();
		
		var win = this.win;
		var dom = this.dom;
		if ($.IE) {
			var sel = this.getSelection(win, dom);
			var range;
			try {
				if (sel.rangeCount > 0) {
					range = sel.getRangeAt(0);
				} else {
					range = sel.createRange ? sel.createRange() : dom.createRange();
				}
			} catch(e) {
				
			}
			if (!range) {
				range = ($.IE) ? dom.body.createTextRange() : dom.createRange();
			}
			
			if (sel.type.toLowerCase() == 'control') {
				range.item(0).outerHTML = html;
			} else {
				range.pasteHTML(html);
			}
		} else {
			dom.execCommand('inserthtml', false, html);
		}
	},
	
	getRange: function(){
		this.init();
		var win = this.win, doc = this.dom, selection, range;

		if (win.getSelection) { // W3C
			selection = win.getSelection();
		
			if (selection.getRangeAt) {
				try{
					range = selection.getRangeAt(0);
				}catch(e){}
			} else { // for Old Webkit! 高版本的已经支持 getRangeAt
				range = doc.createRange();
				range.setStart(selection.anchorNode, selection.anchorOffset);
				range.setEnd(selection.focusNode, selection.focusOffset);
			}

		} else if (doc.selection) { // IE
			range = doc.selection.createRange();
		}
		//if(!range)range = $.IE ? doc.body.createTextRange() : doc.createRange();
		return range;
	},
	
	//获取光标处的元素
	getElementByCursor: function(){
		this.init();
		var dom = this.dom;
		var win = this.win;
		var range = this.getRange();
		if($.IE){
			var sel = this.getSelection(win, dom);
			switch (sel.type) {
			    case "Text":
			    case "None":
					return range.parentElement();
			    case "Control":
					return range.item(0);
			    default:
					return dom.body;
			}
		}else try{
			var p = range.commonAncestorContainer;
			if (!range.collapsed && range.startContainer == range.endContainer 
				&& range.startOffset - range.endOffset <= 1 && range.startContainer.hasChildNodes()
			){
				p = range.startContainer.childNodes[range.startOffset];
			}
			while (p.nodeType == 3) {
				p = p.parentNode;
			}
			return p;
		}catch(e){
			return null;
		}
	},
	getCommonAncestor: function(range) {
		return range.startContainer || // w3c
			   (range.parentElement && range.parentElement()) || // ms TextRange
			   (range.commonParentElement && range.commonParentElement()); // ms IHTMLControlRange
	},
	saveRange: function() {
		this.init();
		$.IE && this.win.focus(); // 确保下面这行 range 是编辑区域的，否则 [Issue 39]
		return this.getRange();
	}
	
});

})($.editor);